using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using Caliburn.Micro;
using System.Linq;
using Action = System.Action;

namespace MediaOwl.Core.Modularity
{
    [Export(typeof(IModuleManager))]
    public class ModuleManager : IModuleManager
    {
        private readonly Dictionary<string , DeploymentCatalog> catalogs;
        private readonly Dictionary<string, Module> modules;

        public ModuleManager()
        {
            catalogs = new Dictionary<string, DeploymentCatalog>();
            modules = new Dictionary<string, Module>();     
        }

        public BindableCollection<Module> Modules
        {
            get
            {
                if (modules.Count > 0)
                    return new BindableCollection<Module>(modules.Select(x => x.Value));
                return new BindableCollection<Module>();
            }
        }

        public IResult LoadModuleList(Action<IEnumerable<Module>> onSuccess = null, Action<Exception> onFail = null)
        {
            return new LoadModuleList(this, onSuccess, onFail);
        }

        public IResult LoadModule(Uri uri, Action onSuccess = null, Action<Exception> onFail = null)
        {
            return new LoadCatalog(uri, this,  onSuccess, onFail);
        }

        public IResult RemoveModule(Uri uri, Action onSuccess = null, Action<Exception> onFail = null)
        {
            return new RemoveCatalog(uri, this, onSuccess, onFail);
        }

        public IResult GetLoadedModules(Action<IEnumerable<Module>> onSuccess = null, Action<Exception> onFail = null)
        {
            return new LoadedModules(this, onSuccess, onFail);
        }

        public DeploymentCatalog GetCatalog(string uri)
        {
            DeploymentCatalog catalog;
            return catalogs.TryGetValue(uri, out catalog) ? catalog : null;
        }

        public DeploymentCatalog GetCatalog(Module module)
        {
            return GetCatalog(module.Uri.AbsoluteUri);
        }

        public Module GetModule(string absoluteUri)
        {
            return modules[absoluteUri];
        }

        internal void RemoveCatalog(string absoluteUri)
        {
            if (catalogs.ContainsKey(absoluteUri))
            {
                catalogs.Remove(absoluteUri);
                modules[absoluteUri].Loaded = false;
            }
        }

        internal void AddCatalog(DeploymentCatalog catalog)
        {
            if (catalogs.ContainsKey(catalog.Uri.ToString()))
                return;
            catalogs.Add(catalog.Uri.ToString(), catalog);
            modules[catalog.Uri.ToString()].Loaded = true;
        }

        internal void SetModules(IEnumerable<Module> moduleList)
        {
            modules.Clear();
            foreach (var module in moduleList)
                modules.Add(module.Uri.ToString(), module);
        }
    }

    public interface IModuleManager
    {
        DeploymentCatalog GetCatalog(string uri);
        DeploymentCatalog GetCatalog(Module module);
        Module GetModule(string absoluteUri);
        IResult LoadModuleList(Action<IEnumerable<Module>> onSuccess = null, Action<Exception> onFail = null);
        IResult LoadModule(Uri uri, Action onSuccess = null, Action<Exception> onFail = null);
        IResult GetLoadedModules(Action<IEnumerable<Module>> onSuccess = null, Action<Exception> onFail = null);
        IResult RemoveModule(Uri uri, Action onSuccess = null, Action<Exception> onFail = null);
        BindableCollection<Module> Modules { get; }
    }
}